﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace SQLite.WinRT.Linq.Base
{
    /// <summary>
    ///     Converts constants into parameters
    /// </summary>
    public class Parameterizer : ExpressionVisitor
    {
        private readonly Func<Expression, bool> fnCanBeParameter;

        private readonly List<ParameterExpression> parameters;

        private readonly List<object> values;

        private Parameterizer(Func<Expression, bool> fnCanBeParameter)
        {
            this.fnCanBeParameter = fnCanBeParameter;
            parameters = new List<ParameterExpression>();
            values = new List<object>();
        }

        public static Expression Parameterize(
            Expression expression,
            Func<Expression, bool> fnCanBeParameter,
            out List<ParameterExpression> parameters,
            out List<object> values)
        {
            var p = new Parameterizer(fnCanBeParameter);
            Expression result = p.Visit(expression);
            parameters = p.parameters;
            values = p.values;
            return result;
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            bool isQueryRoot = c.Value is IQueryable;

            if (!isQueryRoot && !fnCanBeParameter(c))
            {
                return c;
            }

            ParameterExpression p = Expression.Parameter(c.Type, "p" + parameters.Count);
            parameters.Add(p);
            values.Add(c.Value);

            // If query root then parameterize it so we pass the value to the compiled query,
            // but don't replace in the tree so it won't try to map this parameter to actual SQL.
            if (isQueryRoot)
            {
                return c;
            }

            return p;
        }
    }
}